﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using VehicleTrajectoryApp;
using System.IO;

namespace WindowsFormsApplication1
{
    public partial class ODTravelTimeStatistics : Form
    {
        public Dictionary<ulong, CriticalOD> criticalODMap = null;

        public MainForm parentForm;
        //public Dictionary<int, VehicleTrajectoryApp.Node> nodeMap;
        private Dictionary<int, List<int>> ODPairMap = new Dictionary<int, List<int>>();

        private List<String> KMLStringList = new List<string>();
        private int initial_origin_zone_id = -1;
        private int initial_dest_zone_id = -1;

        private DataTable odTable = new DataTable();
        private DataTable pathTable = new DataTable();

        public int NumOfScenarios;
        public string[] ScenarioNames;
        public double[] ScenarioWeights;
        public List<DYNASMARTVehicle>[] ScenarioSpecificVehicleList;

        public int AnalysisStartTime = 0;
        public int AnalysisEndTime = 6;

        public List<IVehicle> gpsTrajectoryList;
        public Dictionary<ulong, Dictionary<int, List<IVehicle>>> gpsVehicles;

        public Dictionary<int, int> DYNASMARTZoneToBPMZoneIDMap;

        private int maxRows = 5000;

        public ODTravelTimeStatistics()
        {
            InitializeComponent();
        }

        public void SpecifyOriginAndDestination(int origin_zone_id, int dest_zone_id)
        {
            //UpdateFormDisplay(origin_zone_id, dest_zone_id, 1);
            initial_origin_zone_id = origin_zone_id;
            initial_dest_zone_id = dest_zone_id;
        }

        private void SetupDataGridViews()
        {
            //Initialize OD datagridview: headers and data type
            List<string> dataGridView1Headers = new List<string>();
            dataGridView1Headers.Add("Origin Zone");
            dataGridView1Headers.Add("Destination Zone");
            dataGridView1Headers.Add("Departure Hour");
            dataGridView1Headers.Add("Total Count(Simulated)");
            dataGridView1Headers.Add("Max Travel Time (min)");
            dataGridView1Headers.Add("Aggregate Average Travel Time (min)(Simulated)");
            dataGridView1Headers.Add("Aggregate Travel Time Std (Simulated)");

            for (int n = 0; n < this.NumOfScenarios; n++)
            {
                dataGridView1Headers.Add(this.ScenarioNames[n] + " Count");
                dataGridView1Headers.Add(this.ScenarioNames[n] + " Average Travel Time (min)");
                dataGridView1Headers.Add(this.ScenarioNames[n] + " Travel Time Std");
            }

            if (gpsVehicles != null)
            {
                dataGridView1Headers.Add("GPS Count");
                dataGridView1Headers.Add("GPS Max Travel Time (min)");
                dataGridView1Headers.Add("GPS Average Travel Time (min)");
                dataGridView1Headers.Add("GPS Travel Time Std");
            }

            odTable.Columns.Add(dataGridView1Headers[0], typeof(int)); //origin
            odTable.Columns.Add(dataGridView1Headers[1], typeof(int)); //destination
            odTable.Columns.Add(dataGridView1Headers[2], typeof(string)); //departure time

            //Total
            odTable.Columns.Add(dataGridView1Headers[3], typeof(int)); // total counts
            odTable.Columns.Add(dataGridView1Headers[4], typeof(double)); // max_travel_time
            odTable.Columns.Add(dataGridView1Headers[5], typeof(double)); // total average
            odTable.Columns.Add(dataGridView1Headers[6], typeof(double)); // total std

            for (int n = 0; n < this.NumOfScenarios; n++)
            {
                odTable.Columns.Add(dataGridView1Headers[7 + n * 3], typeof(int)); // counts
                odTable.Columns.Add(dataGridView1Headers[8 + n * 3], typeof(double)); // average
                odTable.Columns.Add(dataGridView1Headers[9 + n * 3], typeof(double)); // std
            }

            if (gpsVehicles != null)
            {
                odTable.Columns.Add(dataGridView1Headers[7 + this.NumOfScenarios * 3], typeof(int)); // counts
                odTable.Columns.Add(dataGridView1Headers[8 + this.NumOfScenarios * 3], typeof(double)); // average
                odTable.Columns.Add(dataGridView1Headers[9 + this.NumOfScenarios * 3], typeof(double)); // average
                odTable.Columns.Add(dataGridView1Headers[10 + this.NumOfScenarios * 3], typeof(double)); // std
            }

            dataGridView1.DataSource = odTable;
            dataGridView1.Columns[0].DefaultCellStyle.Format = "N0";
            dataGridView1.Columns[1].DefaultCellStyle.Format = "N0";

            dataGridView1.Columns[3].DefaultCellStyle.Format = "N0";
            dataGridView1.Columns[4].DefaultCellStyle.Format = "N2";
            dataGridView1.Columns[5].DefaultCellStyle.Format = "N2";
            dataGridView1.Columns[6].DefaultCellStyle.Format = "N2";
            for (int n = 0; n < this.NumOfScenarios; n++)
            {
                dataGridView1.Columns[7 + n * 3].DefaultCellStyle.Format = "N0";
                dataGridView1.Columns[8 + n * 3].DefaultCellStyle.Format = "N2";
                dataGridView1.Columns[9 + n * 3].DefaultCellStyle.Format = "N2";
            }

            if (gpsVehicles != null)
            {
                dataGridView1.Columns[this.NumOfScenarios * 3 + 7].DefaultCellStyle.Format = "N0";
                dataGridView1.Columns[this.NumOfScenarios * 3 + 8].DefaultCellStyle.Format = "N2";
                dataGridView1.Columns[this.NumOfScenarios * 3 + 9].DefaultCellStyle.Format = "N2";
                dataGridView1.Columns[this.NumOfScenarios * 3 + 10].DefaultCellStyle.Format = "N2";
            }

            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;

            //Initialize path datagridview
            List<string> dataGridView2Headers = new List<string>();
            dataGridView2Headers.Add("Path");
            dataGridView2Headers.Add("Type");
            dataGridView2Headers.Add("Time");
            dataGridView2Headers.Add("Path Length (miles)");

            for (int n = 0; n < this.NumOfScenarios; n++)
            {
                dataGridView2Headers.Add(this.ScenarioNames[n] + " Count");
                dataGridView2Headers.Add(this.ScenarioNames[n] + " Average Travel Time (min)");               
                dataGridView2Headers.Add(this.ScenarioNames[n] + " Travel Time Std");
                dataGridView2Headers.Add(this.ScenarioNames[n] + " Average Travel Speed (mph)");
                dataGridView2Headers.Add(this.ScenarioNames[n] + " Travel Speed Std");
            }

            if (gpsVehicles != null)
            {
                dataGridView2Headers.Add("GPS Count");
                dataGridView2Headers.Add("GPS Average Travel Time (min)");
                dataGridView2Headers.Add("GPS Travel Time Std");
                dataGridView2Headers.Add("GPS Average Travel Speed (mph)");
                dataGridView2Headers.Add("GPS Travel Speed Std");                
            }

            //Path
            pathTable.Columns.Add(dataGridView2Headers[0], typeof(int)); //Path
            pathTable.Columns.Add(dataGridView2Headers[1], typeof(string)); // trajectory type
            pathTable.Columns.Add(dataGridView2Headers[2], typeof(DateTime)); // departure time
            pathTable.Columns.Add(dataGridView2Headers[3], typeof(double)); //Distance

            for (int n = 0; n < this.NumOfScenarios; n++)
            {
                pathTable.Columns.Add(dataGridView2Headers[4 + n * 5], typeof(int)); // counts
                pathTable.Columns.Add(dataGridView2Headers[5 + n * 5], typeof(double)); // average travel time
                pathTable.Columns.Add(dataGridView2Headers[6 + n * 5], typeof(double)); // travel timestd
                pathTable.Columns.Add(dataGridView2Headers[7 + n * 5], typeof(double)); // average speed
                pathTable.Columns.Add(dataGridView2Headers[8 + n * 5], typeof(double)); // speed std
            }

            if (gpsVehicles != null)
            {
                pathTable.Columns.Add(dataGridView2Headers[4 + this.NumOfScenarios * 5], typeof(int)); // count
                pathTable.Columns.Add(dataGridView2Headers[5 + this.NumOfScenarios * 5], typeof(double)); // average travel time
                pathTable.Columns.Add(dataGridView2Headers[6 + this.NumOfScenarios * 5], typeof(double)); // travel time std
                pathTable.Columns.Add(dataGridView2Headers[7 + this.NumOfScenarios * 5], typeof(double)); // average speed
                pathTable.Columns.Add(dataGridView2Headers[8 + this.NumOfScenarios * 5], typeof(double)); // speed std
            }

            dataGridView2.DataSource = pathTable;

            dataGridView2.Columns[0].DefaultCellStyle.Format = "N0"; // path index
            dataGridView2.Columns[3].DefaultCellStyle.Format = "N2"; // distance

            for (int n = 0; n < this.NumOfScenarios; n++)
            {
                dataGridView2.Columns[4 + n * 5].DefaultCellStyle.Format = "N0"; // count
                dataGridView2.Columns[5 + n * 5].DefaultCellStyle.Format = "N2"; // average travel time
                dataGridView2.Columns[6 + n * 5].DefaultCellStyle.Format = "N2"; // travel time std
                dataGridView2.Columns[7 + n * 5].DefaultCellStyle.Format = "N2"; // average speed
                dataGridView2.Columns[8 + n * 5].DefaultCellStyle.Format = "N2"; // speed std
            }

            if (gpsVehicles != null)
            {
                dataGridView2.Columns[this.NumOfScenarios * 5 + 4].DefaultCellStyle.Format = "N0"; //count
                dataGridView2.Columns[this.NumOfScenarios * 5 + 5].DefaultCellStyle.Format = "N2"; //average travel time
                dataGridView2.Columns[this.NumOfScenarios * 5 + 6].DefaultCellStyle.Format = "N2"; // travel time std
                dataGridView2.Columns[this.NumOfScenarios * 5 + 7].DefaultCellStyle.Format = "N2"; // average travel speed
                dataGridView2.Columns[this.NumOfScenarios * 5 + 8].DefaultCellStyle.Format = "N2"; // speed std
            }

            dataGridView2.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
        }

        private void ODTravelTimeStatistics_Load(object sender, EventArgs e)
        {
            SetupDataGridViews();
            for (int i = 0; i < 21; i++)
            {
                this.comboBox4.Items.Add(i.ToString());
            }
            this.comboBox4.Items.Add("> 25");
            this.comboBox4.Items.Add("> 30");
            this.comboBox4.Items.Add("> 35");
            this.comboBox4.Items.Add("> 40");
            this.comboBox4.SelectedIndex = 0;

            this.comboBox5.Items.Add("0");
            this.comboBox5.Items.Add("> 5");
            this.comboBox5.Items.Add("> 10");
            this.comboBox5.Items.Add("> 20");
            this.comboBox5.Items.Add("> 30");
            this.comboBox5.SelectedIndex = 0;

            for (int i = 0; i < 21; i++)
            {
                this.comboBox6.Items.Add(i.ToString());
            }
            this.comboBox6.Items.Add("> 25");
            this.comboBox6.Items.Add("> 30");
            this.comboBox6.Items.Add("> 35");
            this.comboBox6.Items.Add("> 40");
            this.comboBox6.SelectedIndex = 1;

            this.comboBox7.SelectedIndex = 0;

            if (!Int32.TryParse(comboBox7.Text, out maxRows))
            {
                maxRows = Int32.MaxValue;
            }

            for (int i = this.AnalysisStartTime; i <= AnalysisEndTime - 1; i++)
            {
                this.comboBox8.Items.Add(i);
            }
            this.comboBox8.SelectedIndex = 0;

            for (int i = AnalysisStartTime + 1; i <= this.AnalysisEndTime; i++)
            {
                this.comboBox9.Items.Add(i);
            }
            this.comboBox9.SelectedIndex = this.comboBox9.Items.Count - 1;

            if (criticalODMap.Keys.Count > 0)
            {
                List<ulong> keyList = criticalODMap.Keys.ToList();
                if (gpsVehicles != null)
                {
                    List<ulong> gpsKeyList = gpsVehicles.Keys.ToList();
                    keyList.AddRange(gpsKeyList);
                    HashSet<ulong> uniqueKeyHashSet = new HashSet<ulong>(keyList);
                    keyList = uniqueKeyHashSet.ToList();
                }

                int origin_zone_id;
                int dest_zone_id;

                for (int i = 0; i < keyList.Count; i++)
                {
                    origin_zone_id = (int)((keyList[i] & 0xffffffff00000000) >> 32);
                    dest_zone_id = (int)(keyList[i] & 0x00000000ffffffff);

                    if (!ODPairMap.ContainsKey(origin_zone_id))
                    {
                        ODPairMap[origin_zone_id] = new List<int>();
                    }

                    if (!ODPairMap[origin_zone_id].Contains(dest_zone_id))
                    {
                        ODPairMap[origin_zone_id].Add(dest_zone_id);
                    }
                }
            }

            List<int> originZoneIDList = ODPairMap.Keys.ToList();
            originZoneIDList.Sort();
            this.comboBox1.Items.Add("All");
            for (int i = 0; i < originZoneIDList.Count; i++)
            {
                this.comboBox1.Items.Add(originZoneIDList[i]);
            }

            if (initial_origin_zone_id != -1) // with specified origin and dest ids
            {
                List<int> destZoneIDList = ODPairMap[initial_origin_zone_id];
                destZoneIDList.Sort();
                this.comboBox2.Items.Add("All");
                for (int i = 0; i < destZoneIDList.Count; i++)
                {
                    this.comboBox2.Items.Add(destZoneIDList[i]);
                }

                int comboBox1Index = 0;
                int comboBox2Index = 0;

                for (int i = 0; i < originZoneIDList.Count; i++)
                {
                    if (originZoneIDList[i] == initial_origin_zone_id)
                    {
                        comboBox1Index = i;
                        break;
                    }
                }

                for (int i = 0; i < destZoneIDList.Count; i++)
                {
                    if (destZoneIDList[i] == initial_dest_zone_id)
                    {
                        comboBox2Index = i;
                        break;
                    }
                }

                this.comboBox1.SelectedIndex = comboBox1Index + 1;
                this.comboBox2.SelectedIndex = comboBox2Index + 1;
            }
            else
            {
                List<int> destZoneIDList = ODPairMap[originZoneIDList[0]];
                this.comboBox2.Items.Add("All");
                //for (int i = 0; i < destZoneIDList.Count; i++)
                //{
                //    this.comboBox2.Items.Add(destZoneIDList[i]);
                //}
                
                this.comboBox1.SelectedIndex = 0;
                this.comboBox2.SelectedIndex = 0;
            }

            this.comboBox3.Items.Add("All");
            DateTime dateTime = new DateTime(2012, 7, 31, AnalysisStartTime, 0, 0);
            for (int i = AnalysisStartTime; i < AnalysisEndTime; i++)
            {
                this.comboBox3.Items.Add(dateTime.ToShortTimeString());
                dateTime = dateTime.AddHours(1);
            }

            radioButton3.Checked = true;

            if (initial_origin_zone_id != -1 && initial_dest_zone_id != -1)
            {
                UpdateFormDisplay(initial_origin_zone_id, initial_dest_zone_id, -1);
                this.comboBox3.SelectedIndex = 0;
            }
            else
            {
                UpdateFormDisplay(-1, -1, AnalysisStartTime);
                this.comboBox3.SelectedIndex = 1;
            }

        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.comboBox1.SelectedIndex == 0)
            {
                this.comboBox2.Items.Clear();
                this.comboBox2.Items.Add("All");
                this.comboBox2.SelectedIndex = 0;
            }
            else
            {
                int origin_zone_id = Int32.Parse(this.comboBox1.SelectedItem.ToString());
                if (ODPairMap.ContainsKey(origin_zone_id))
                {
                    this.comboBox2.Items.Clear();

                    List<int> destZoneIDList = ODPairMap[origin_zone_id];
                    destZoneIDList.Sort();
                    this.comboBox2.Items.Add("All");
                    for (int i = 0; i < destZoneIDList.Count; i++)
                    {
                        this.comboBox2.Items.Add(destZoneIDList[i]);
                    }

                    this.comboBox2.SelectedIndex = 0;
                }
            }
        }

        private bool AddODStatisticsToDataTable(int origin_zone_id, int dest_zone_id, int departure_hour, out int simulated_counts, out int gps_count)
        {
            int[] filter = new int[this.comboBox5.Items.Count];
            filter[0] = 0;
            filter[1] = 5;
            filter[2] = 10;
            filter[3] = 20;
            filter[4] = 30;

            int simulatedCount_filter = 0;
            if (this.comboBox4.SelectedIndex < 20)
            {
                simulatedCount_filter = this.comboBox4.SelectedIndex;
            }
            else
            {
                simulatedCount_filter = 20 + (this.comboBox4.SelectedIndex - 20) * 5;
            }

            int gpsCount_filter = 0;
            if (this.comboBox6.SelectedIndex < 20)
            {
                gpsCount_filter = this.comboBox6.SelectedIndex;
            }
            else
            {
                gpsCount_filter = 20 + (this.comboBox6.SelectedIndex - 20) * 5;
            }

            int travel_time_filter = filter[this.comboBox5.SelectedIndex];

            simulated_counts = 0;
            gps_count = 0;

            if (origin_zone_id != -1 && dest_zone_id != -1)
            {
                CriticalOD odData = null;
                ulong key = ((ulong)origin_zone_id) << 32 | (uint)(dest_zone_id);
                if (criticalODMap.ContainsKey(key))
                {
                    odData = criticalODMap[key];
                }
                else
                {
                    odData = null;
                }

                int counts;
                float average;
                float std;
                float max_travel_time;

                List<object> rowContents = new List<object>();


                if (odData == null && !(gpsVehicles.ContainsKey(key) && gpsVehicles[key].ContainsKey(departure_hour)))
                {
                    return false;
                }

                if (!(odData != null && odData.odSummaryMap.ContainsKey(departure_hour)) && !(gpsVehicles.ContainsKey(key) && gpsVehicles[key].ContainsKey(departure_hour)))
                {
                    return false;
                }

                if (odData == null && !radioButton3.Checked)
                {
                    return false;
                }

                rowContents.Clear();
                rowContents.Add(origin_zone_id);
                rowContents.Add(dest_zone_id);

                DateTime dateTime = new DateTime(2012, 7, 31, departure_hour, 0, 0);
                rowContents.Add(dateTime.ToShortTimeString());

                if (odData != null && odData.odSummaryMap.ContainsKey(departure_hour))
                {
                    odData.odSummaryMap[departure_hour].GetGeneralSummary(out counts, out average, out std, out max_travel_time);
                    rowContents.Add(counts);
                    rowContents.Add(max_travel_time);
                    rowContents.Add(average);
                    rowContents.Add(std);

                    simulated_counts += counts;

                    if (counts <= 0 && !radioButton3.Checked)
                    {
                        return false;
                    }

                    if (radioButton3.Checked)
                    {
                        if (counts < simulatedCount_filter)
                        {
                            return false;
                        }

                        if (average < travel_time_filter)
                        {
                            return false;
                        }
                    }

                    for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                    {
                        odData.odSummaryMap[departure_hour].GetSummaryByScenario(scenario, out counts, out average, out std);
                        if (counts <= 0 && !radioButton3.Checked)
                        {
                            return false;
                        }
                        rowContents.Add(counts);
                        rowContents.Add(average);
                        rowContents.Add(std);
                    }
                }
                else
                {
                    if (!radioButton3.Checked)
                    {
                        return false;
                    }
                    counts = 0;
                    average = std = max_travel_time = 0;

                    rowContents.Add(counts);
                    rowContents.Add(max_travel_time);
                    rowContents.Add(average);
                    rowContents.Add(std);

                    if (counts < simulatedCount_filter)
                    {
                        return false;
                    }

                    if (average < travel_time_filter)
                    {
                        return false;
                    }

                    for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                    {
                        rowContents.Add(counts);
                        rowContents.Add(average);
                        rowContents.Add(std);
                    }
                }

                if (gpsVehicles != null)
                {
                    counts = 0;
                    average = std = max_travel_time = 0;

                    if (gpsVehicles.ContainsKey(key) && gpsVehicles[key].ContainsKey(departure_hour))
                    {
                        List<IVehicle> vehList = gpsVehicles[key][departure_hour];
                        foreach (IVehicle v in vehList)
                        {
                            if (max_travel_time < v.VehicleTravelTime)
                            {
                                max_travel_time = v.VehicleTravelTime;
                            }
                            average += v.VehicleTravelTime;
                            std += v.VehicleTravelTime * v.VehicleTravelTime;
                            counts++;
                        }

                        gps_count += counts;

                        if (counts < gpsCount_filter)
                        {
                            return false;
                        }

                        if (counts > 1)
                        {
                            std = (float)(Math.Sqrt((std - (average * average) / counts) / (counts - 1)));
                            average = average / counts;
                        }
                        else
                        {
                            std = 0;
                        }
                    }
                    else
                    {
                        if (radioButton1.Checked)
                        {
                            return false;
                        }

                        if (radioButton3.Checked && counts < gpsCount_filter)
                        {
                            return false;
                        }
                    }

                    rowContents.Add(counts);
                    rowContents.Add(max_travel_time);
                    rowContents.Add(average);
                    rowContents.Add(std);
                }

                odTable.Rows.Add(rowContents.ToArray());

                return true;

            }
            else
            {
                return false;
            }
        }

        private void UpdateFormDisplay(int origin_zone_id, int dest_zone_id, int departure_hour)
        {
            Cursor = Cursors.WaitCursor;

            odTable.Rows.Clear();
            int start_hour;
            int end_hour;
            if (departure_hour == -1)
            {
                start_hour = AnalysisStartTime;
                end_hour = AnalysisEndTime;
            }
            else
            {
                start_hour = departure_hour;
                end_hour = departure_hour + 1;
            }

            int numOfRows = 0;
            int total_Simulated = 0;
            int total_GPS = 0;
            int tmp_SimulatedCounts = 0;
            int tmp_GPSCounts = 0;

            if (origin_zone_id != -1 && dest_zone_id != -1)
            {
                for (int hour = start_hour; hour < end_hour; hour++)
                {
                    if (AddODStatisticsToDataTable(origin_zone_id, dest_zone_id, hour,out tmp_SimulatedCounts, out tmp_GPSCounts))
                    {
                        numOfRows++;

                        total_Simulated += tmp_SimulatedCounts;
                        total_GPS += tmp_GPSCounts;

                        if (numOfRows >= maxRows)
                        {
                            break;
                        }
                    }
                }

            }

            if (origin_zone_id == -1 && dest_zone_id != -1)
            {
                List<ulong> keyList = criticalODMap.Keys.ToList();
                if (gpsVehicles != null)
                {
                    List<ulong> gpsKeyList = gpsVehicles.Keys.ToList();
                    keyList.AddRange(gpsKeyList);
                    HashSet<ulong> uniqueKeyHashSet = new HashSet<ulong>(keyList);
                    keyList = uniqueKeyHashSet.ToList();
                }
                List<ulong> filterKeyList = new List<ulong>();

                for (int i = 0; i < keyList.Count; i++)
                {
                    if ((int)(keyList[i] & 0x00000000ffffffff) == dest_zone_id)
                    {
                        filterKeyList.Add(keyList[i]);
                    }
                }

                for (int i = 0; i < filterKeyList.Count; i++)
                {
                    ulong key = filterKeyList[i];

                    int OriginZone = (int)((key & 0xffffffff00000000) >> 32);
                    int DestZone = (int)(key & 0x00000000ffffffff);

                    List<object> rowContents = new List<object>();

                    if (numOfRows >= maxRows)
                    {
                        break;
                    }

                    for (int hour = start_hour; hour < end_hour; hour++)
                    {
                        if (AddODStatisticsToDataTable(OriginZone, DestZone, hour, out tmp_SimulatedCounts, out tmp_GPSCounts))
                        {
                            numOfRows++;

                            total_Simulated += tmp_SimulatedCounts;
                            total_GPS += tmp_GPSCounts;

                            if (numOfRows >= maxRows)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (origin_zone_id != -1 && dest_zone_id == -1)
            {
                List<ulong> keyList = criticalODMap.Keys.ToList();
                if (gpsVehicles != null)
                {
                    List<ulong> gpsKeyList = gpsVehicles.Keys.ToList();
                    keyList.AddRange(gpsKeyList);
                    HashSet<ulong> uniqueKeyHashSet = new HashSet<ulong>(keyList);
                    keyList = uniqueKeyHashSet.ToList();
                }

                List<ulong> filterKeyList = new List<ulong>();

                for (int i = 0; i < keyList.Count; i++)
                {
                    if ((int)((keyList[i] & 0xffffffff00000000) >> 32) == origin_zone_id)
                    {
                        filterKeyList.Add(keyList[i]);
                    }
                }

                for (int i = 0; i < filterKeyList.Count; i++)
                {
                    ulong key = filterKeyList[i];
                    int OriginZone = (int)((key & 0xffffffff00000000) >> 32);
                    int DestZone = (int)(key & 0x00000000ffffffff);

                    List<object> rowContents = new List<object>();

                    if (numOfRows >= maxRows)
                    {
                        break;
                    }

                    for (int hour = start_hour; hour < end_hour; hour++)
                    {

                        if (AddODStatisticsToDataTable(OriginZone, DestZone, hour, out tmp_SimulatedCounts, out tmp_GPSCounts))
                        {
                            numOfRows++;

                            total_Simulated += tmp_SimulatedCounts;
                            total_GPS += tmp_GPSCounts;

                            if (numOfRows >= maxRows)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (origin_zone_id == -1 && dest_zone_id == -1)
            {
                List<ulong> keyList = criticalODMap.Keys.ToList();
                if (gpsVehicles != null)
                {
                    List<ulong> gpsKeyList = gpsVehicles.Keys.ToList();
                    keyList.AddRange(gpsKeyList);
                    HashSet<ulong> uniqueKeyHashSet = new HashSet<ulong>(keyList);
                    keyList = uniqueKeyHashSet.ToList();
                }
                keyList.Sort();

                for (int i = 0; i < keyList.Count; i++)
                {
                    ulong key = keyList[i];
                    int OriginZone = (int)((key & 0xffffffff00000000) >> 32);
                    int DestZone = (int)(key & 0x00000000ffffffff);

                    List<object> rowContents = new List<object>();

                    if (numOfRows >= maxRows)
                    {
                        break;
                    }

                    for (int hour = start_hour; hour < end_hour; hour++)
                    {
                        if (AddODStatisticsToDataTable(OriginZone, DestZone, hour, out tmp_SimulatedCounts, out tmp_GPSCounts))
                        {
                            numOfRows++;

                            total_Simulated += tmp_SimulatedCounts;
                            total_GPS += tmp_GPSCounts;

                            if (numOfRows >= maxRows)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            label7.Text = string.Format("Total number of simulated vehicles loaded = {0}\nTotal number of GPS traces loaded = {1}", total_Simulated, total_GPS);
            Cursor = Cursors.Default;
        }
        private void button1_Click(object sender, EventArgs e)
        {
            odTable.Rows.Clear();
            pathTable.Rows.Clear();

            int origin_zone_id = -1;
            if (this.comboBox1.SelectedIndex > 0)
            {
                origin_zone_id = Int32.Parse(this.comboBox1.SelectedItem.ToString());
            }

            int dest_zone_id = -1;
            if (this.comboBox2.SelectedIndex > 0)
            {
                dest_zone_id = Int32.Parse(this.comboBox2.SelectedItem.ToString());
            }

            int departure_hour = -1;
            if (this.comboBox3.SelectedIndex > 0)
            {
                departure_hour = this.AnalysisStartTime + this.comboBox3.SelectedIndex - 1;
            }

            UpdateFormDisplay(origin_zone_id, dest_zone_id, departure_hour);
        }

        private void dataGridView1_SelectionChanged(object sender, EventArgs e)
        {
            
            pathTable.Rows.Clear();

            int selectedRowIndex = -1;
            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                if (dataGridView1.Rows[i].Selected)
                {
                    selectedRowIndex = i;
                    break;
                }
            }
            if (selectedRowIndex != -1)
            {
                int origin_zone_id = Int32.Parse(dataGridView1.Rows[selectedRowIndex].Cells[0].Value.ToString());
                int dest_zone_id = Int32.Parse(dataGridView1.Rows[selectedRowIndex].Cells[1].Value.ToString());
                DateTime dateTime = DateTime.ParseExact(dataGridView1.Rows[selectedRowIndex].Cells[2].Value.ToString(), "h:mm tt", null);
                int departure_hour = dateTime.Hour;

                CriticalOD odData = null;
                ulong key = ((ulong)origin_zone_id) << 32 | (uint)(dest_zone_id);
                if (criticalODMap.ContainsKey(key))
                {
                    odData = criticalODMap[key];
                }

                int counts;
                float average;
                float std;

                float average_speed;
                float speed_std;

                List<object> rowContents = new List<object>();

                KMLStringList.Clear();

                int truePathIndex = 0;

                string[] kmlColorString = { "FF14F000", "FFF00014", "FF1400FF", "FF00C814", "FF14B4FA", "FFF0B414", "FF1400BE", "FFFF7800", "FF78BEF0", "FFB4E614" };

                if (odData != null && odData.odSummaryMap.ContainsKey(departure_hour))
                {
                    for (int pathIndex = 0; pathIndex < odData.odSummaryMap[departure_hour].pathList.Count; pathIndex++)
                    {
                        rowContents.Clear();
                        odData.odSummaryMap[departure_hour].GetSummaryByPathIndex(pathIndex, out counts, out average, out std);
                        if (counts == 0)
                        {
                            continue;
                        }

                        String name = String.Format("[{0}->{1}]-Path-{2}", origin_zone_id, dest_zone_id, truePathIndex + 1);
                        string ColorString = kmlColorString[(pathIndex + 5) % kmlColorString.Length];

                        rowContents.Add(truePathIndex + 1);
                        if (odData.odSummaryMap[departure_hour].pathList[pathIndex].type == VehicleTrajectoryType.DYNASMART)
                        {
                            KMLStringList.Add(ConvertNodeListToKmlString(name, ColorString, odData.odSummaryMap[departure_hour].pathList[pathIndex].nodeList, parentForm.nodeMap));
                            rowContents.Add("DYNASMART");
                            rowContents.Add(new DateTime(2012, 1, 1));
                        }
                        else
                        {
                            KMLStringList.Add(odData.odSummaryMap[departure_hour].pathList[pathIndex].ConvertVehicleTraceToKMLString(name, ColorString));
                            rowContents.Add("Aimsun");
                            rowContents.Add(new DateTime(2012, 7, 18));
                        }

                        rowContents.Add(odData.odSummaryMap[departure_hour].pathList[pathIndex].path_distance_in_mile);

                        for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                        {
                            odData.odSummaryMap[departure_hour].GetSummaryByScenarioAndPathIndex(scenario, pathIndex, out counts, out average, out std, out average_speed, out speed_std);
                            rowContents.Add(counts);
                            rowContents.Add(average);
                            rowContents.Add(std);
                            rowContents.Add(average_speed);
                            rowContents.Add(speed_std);                            
                        }

                        if (gpsVehicles != null)
                        {
                            rowContents.Add(0);
                            rowContents.Add(0);
                            rowContents.Add(0);
                            rowContents.Add(0);
                            rowContents.Add(0);
                        }

                        pathTable.Rows.Add(rowContents.ToArray());
                        truePathIndex++;
                    }
                }

                if (gpsVehicles != null && gpsVehicles.ContainsKey(key) && gpsVehicles[key].ContainsKey(departure_hour))
                {
                    List<IVehicle> vehList = gpsVehicles[key][departure_hour];
                    int n = 0;
                    foreach (IVehicle v in vehList)
                    {
                        rowContents.Clear();
                        n++;
                        rowContents.Add(truePathIndex + 1);
                        rowContents.Add("GPS");
                        rowContents.Add(v.VehicleStartTime);
                        rowContents.Add(v.Travel_Distance_In_Mile);

                        for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                        {
                            rowContents.Add(0);
                            rowContents.Add(0);
                            rowContents.Add(0);
                            rowContents.Add(0);
                            rowContents.Add(0);
                        }

                        rowContents.Add(1);
                        rowContents.Add(v.VehicleTravelTime);
                        rowContents.Add(0);
                        rowContents.Add(v.Travel_Distance_In_Mile / v.VehicleTravelTime * 60);
                        rowContents.Add(0);

                        string ColorString = kmlColorString[n % kmlColorString.Length];

                        KMLStringList.Add(((GPSTrajectory)v).ConvertVehicleTraceToKMLString(String.Format("[{0}->{1}]-Path-{2}(GPS)", v.OriginZoneID, v.DestinationZoneID, truePathIndex + 1), ColorString));
                        pathTable.Rows.Add(rowContents.ToArray());
                        truePathIndex++;
                    }
                }
            }
           
        }

        private void dataGridView2_SelectionChanged(object sender, EventArgs e)
        {
            int index = -1;
            List<string> kmlStringList = new List<string>();
            for (int i = 0; i < dataGridView2.Rows.Count; i++)
            {
                if (dataGridView2.Rows[i].Selected)
                {
                    index = i;
                    int pathIndex = Int32.Parse(dataGridView2.Rows[index].Cells[0].Value.ToString());
                    pathIndex = pathIndex - 1;
                    if (pathIndex < KMLStringList.Count && pathIndex >= 0)
                    {
                        kmlStringList.Add(KMLStringList[pathIndex]);
                    }
                }
            }

            parentForm.AddPathOnMap(kmlStringList);
        }

        private string ConvertNodeListToKmlString(string name, string colorCode, List<int> nodeList, Dictionary<int, VehicleTrajectoryApp.Node> nodeMap)
        {
            string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
            s += "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
            s += "<Document>";
            s += "<name>" + name + "</name>\n";                    
            
            s += "<Placemark>\n";
            s += "<name>" + name + "</name>";
            s += "<Style><LineStyle><width>4</width><gx:labelVisibility>1</gx:labelVisibility><color>";
            //s += "ffff0000";
            s += colorCode;
            s += "</color></LineStyle><IconStyle><scale>0</scale></IconStyle><PolyStyle><fill>0</fill></PolyStyle></Style>\n";
            s += "<MultiGeometry>";
            s += "<LineString>\n";
            s += "<extrude>0</extrude>\n";
            s += "<tessellate>0</tessellate>\n";
            s += "<altitudeMode>relativeToGround</altitudeMode>\n";
            s += "<coordinates>\n";
            for (int i = 0; i < nodeList.Count - 1; i++)
            {
                if (nodeMap.ContainsKey(nodeList[i]) && nodeMap.ContainsKey(nodeList[i + 1]))
                {
                    VehicleTrajectoryApp.Node startNode = nodeMap[nodeList[i]];
                    VehicleTrajectoryApp.Node endNode = nodeMap[nodeList[i + 1]];
                    s += String.Format("{0},{1},0 {2},{3},0\n", startNode.Longitude, startNode.Latitude, endNode.Longitude, endNode.Latitude);
                }
            }

            s += "</coordinates>\n";
            s += "</LineString>\n";
            s += "<Point>";
            s += "<coordinates>";
            s += String.Format("{0},{1},0\n", nodeMap[nodeList[0]].Longitude, nodeMap[nodeList[0]].Latitude);
            s += "</coordinates>";
            s += "</Point>\n";
            s += "</MultiGeometry>\n";
            s += "</Placemark>\n";
            s += "</Document>";
            s += "</kml>";
            return s; 
        }

        private void ODTravelTimeStatistics_FormClosing(object sender, FormClosingEventArgs e)
        {
            parentForm.RemoveKmlObj();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            int selectedRowIndex = -1;
            int origin_zone_id = -1;
            int dest_zone_id = -1;

            int targetStartHour = Int32.Parse(this.comboBox8.Text);
            int targetEndHour = Int32.Parse(this.comboBox9.Text);

            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                if (dataGridView1.Rows[i].Selected)
                {
                    selectedRowIndex = i;
                    origin_zone_id = Int32.Parse(dataGridView1.Rows[i].Cells[0].Value.ToString());
                    dest_zone_id = Int32.Parse(dataGridView1.Rows[i].Cells[1].Value.ToString());
                    break;
                }
            }

            if (origin_zone_id != -1 && dest_zone_id != -1)
            {
                ulong key = ((ulong)origin_zone_id << 32) | (uint)(dest_zone_id);

                ODStatisticsChartForm form = new ODStatisticsChartForm();
                if (criticalODMap.ContainsKey(key))
                {
                    form.odData = criticalODMap[key];
                }
                else
                {
                    form.odData = null;
                }

                form.origin_zone_id = origin_zone_id;
                form.destination_zone_id = dest_zone_id;
                form.NumOfScenarios = this.NumOfScenarios;
                form.ScenarioNames = this.ScenarioNames;
                form.ScenarioWeights = this.ScenarioWeights;
                form.AnalysisStartHour = targetStartHour;
                form.AnalysisEndHour = targetEndHour;
                form.parentForm = this.parentForm;

                if (gpsVehicles.ContainsKey(key))
                {
                    form.vehicleMap = gpsVehicles[key];
                }
                else
                {
                    form.vehicleMap = null;
                }
                form.ShowDialog();

            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            string exportFileName = parentForm.projectFolder + @"\" + "Exported_Statistics.csv";
            if (criticalODMap != null)
            {
                using (StreamWriter writer = new StreamWriter(exportFileName))
                {
                    for (int i = 0; i < dataGridView1.Columns.Count; i++)
                    {
                        if (i != dataGridView1.Columns.Count - 1)
                        {
                            writer.Write(dataGridView1.Columns[i].HeaderText + ",");
                        }
                        else
                        {
                            writer.WriteLine(dataGridView1.Columns[i].HeaderText);
                        }
                    }

                    for (int i = 0; i < dataGridView1.Rows.Count; i++)
                    {
                        for (int j = 0; j < dataGridView1.Rows[i].Cells.Count; j++)
                        {
                            writer.Write(dataGridView1.Rows[i].Cells[j].Value);
                            if (j != dataGridView1.Rows[i].Cells.Count - 1)
                            {
                                writer.Write(",");
                            }
                            else
                            {
                                writer.WriteLine();
                            }
                        }
                    }
                }
            }
            
            MessageBox.Show(this, "The exported summary statistics file is stored as " + exportFileName + "\n");
            System.Diagnostics.Process.Start(parentForm.projectFolder);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                for (int i = 3; i < 7; i++)
                {
                    dataGridView1.Columns[i].Visible = false;
                }

                for (int i = 0; i < NumOfScenarios; i++)
                {
                    dataGridView1.Columns[7 + i * 3].Visible = false;
                    dataGridView1.Columns[8 + i * 3].Visible = false;
                    dataGridView1.Columns[9 + i * 3].Visible = false;
                }
            }
            else
            {
                for (int i = 3; i < 7; i++)
                {
                    dataGridView1.Columns[i].Visible = true;
                }

                for (int i = 0; i < NumOfScenarios; i++)
                {
                    dataGridView1.Columns[7 + i * 3].Visible = true;
                    dataGridView1.Columns[8 + i * 3].Visible = true;
                    dataGridView1.Columns[9 + i * 3].Visible = true;
                }
            }
        }

        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox2.Checked)
            {
                int n = dataGridView1.Columns.Count;

                for (int i = 0; i < 4; i++)
                {
                    dataGridView1.Columns[n - 1 - i].Visible = false;
                }
            }
            else
            {
                int n = dataGridView1.Columns.Count;

                for (int i = 0; i < 4; i++)
                {
                    dataGridView1.Columns[n - 1 - i].Visible = true;
                }
            }
        }

        private void comboBox7_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!Int32.TryParse(comboBox7.Text, out maxRows))
            {
                maxRows = Int32.MaxValue;
            }
        }

        private void comboBox8_SelectedIndexChanged(object sender, EventArgs e)
        {
            int hour = Int32.Parse(this.comboBox8.Text);

            this.comboBox9.Items.Clear();
            for (int i = hour + 1; i <= AnalysisEndTime; i++)
            {
                this.comboBox9.Items.Add(i);
            }
            this.comboBox9.SelectedIndex = this.comboBox9.Items.Count - 1;
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked)
            {
                comboBox4.Enabled = comboBox5.Enabled = comboBox6.Enabled = false;
            }
        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton2.Checked)
            {
                comboBox4.Enabled = comboBox5.Enabled = comboBox6.Enabled = false;
            }
        }

        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton3.Checked)
            {
                comboBox4.Enabled = comboBox5.Enabled = comboBox6.Enabled = Enabled;
            }
        }

    }
}
